/*!
 * 将平铺的文件数组数据转换成树形结构，clone自 https://github.com/nknapp/tree-from-paths
 *
 * tree-from-paths <https://github.com/nknapp/tree-from-paths>
 *
 * Copyright (c) 2017 Nils Knappmeier.
 * Released under the MIT license.
 */

import groupBy from 'lodash.groupby';

/**
 * Transform an array of paths into an [archy](https://www.npmjs.com/package/archy)-compatible tree structure.
 *
 * ```
 * ['abc/cde/efg/', 'abc/cde/abc', 'abc/zyx']
 * ```
 * becomes
 * ```
 * {
 *   label: 'abc/',
 *   nodes: [{ label: 'cde/',nodes: ['efg/', 'abc']}, 'zyx']
 * }
 * ```
 *
 * Nodes with a single subnode are collapsed and the resulting node gets the label `node/subnode`.
 *
 * @param {string[]} files an array of sorted file paths relative to `parent`
 * @param {string} baseDir the directory of the files
 * @param {function(parent:string, file:string, explicit: boolean):string} renderLabelFn function that renders the label
 *  of a node. It receives the parent and a filenpath as parameters.
 * @param {object=} options optional parameters
 * @param {string=} options.label the label of the root node (default: '')
 * @returns {object} a tree structure as needed by [archy](https://www.npmjs.com/package/archy)
 * @access private
 */
function treeFromPaths(files, baseDir, renderLabelFn, options = {}, nodesStr = 'nodes') {
  return {
    label: options.label || '',
    [nodesStr]: childNodesFromPaths(files, baseDir, renderLabelFn, files, baseDir, nodesStr),
  };
}

/**
 * Compute the child nodes of a node, given a list of paths
 * @param files
 * @param parent
 * @param renderLabelFn
 * @returns {Array}
 */
function childNodesFromPaths(files, parent, renderLabelFn, originalFiles, baseDir, nodesStr) {
  // Group by first path element
  const groups = groupBy(files, (file) => file.match(/^[^/]*\/?/));

  return Object.keys(groups)
    .sort()
    .map((groupKey) => {
      const group = groups[groupKey];
      // Is this group explicitly part of the result, or
      // just implicit through its children
      const explicit = group.indexOf(groupKey) >= 0;
      let index = -1;
      if (explicit) {
        index = originalFiles.indexOf(parent.replace(baseDir, '') + groupKey);
      }

      return {
        label: renderLabelFn(parent, groupKey, explicit, explicit ? index : -1),
        [nodesStr]: childNodesFromPaths(
          // Remove parent directory from file paths
          group
            .map((node) => node.substr(groupKey.length))
            // Skip the empty path
            .filter((node) => node),
          // New parent..., normalize to one trailing slash
          parent + groupKey,
          renderLabelFn,
          originalFiles,
          baseDir,
          nodesStr,
        ),
      };
    });
}

export default treeFromPaths;
